/*
 * 项目名称:keel-flower
 * 类名称:UserRealm.java
 * 包名称:me.keelflower.realm
 *
 * 修改履历:
 *      日期                修正者      主要内容
 *      2020/12/15 23:17    liliudong      初版完成
 *
 */
package com.liliudong.keelflower.upms.admin.auth;

import cn.hutool.core.convert.Convert;
import com.liliudong.keelflower.common.core.constants.Const;
import com.liliudong.keelflower.common.core.utils.CacheUtils;
import com.liliudong.keelflower.upms.biz.entity.sys.SysUser;
import com.liliudong.keelflower.upms.biz.service.sys.SysMenuService;
import com.liliudong.keelflower.upms.biz.service.sys.SysRoleService;
import com.liliudong.keelflower.upms.biz.service.sys.SysUserService;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author liliudong 2020/12/15 23:17
 */
@Component
@RequiredArgsConstructor
public class SysUserRealm extends AuthorizingRealm {

    private final SysUserService sysUserService;
    private final SysRoleService sysRoleService;
    private final SysMenuService sysMenuService;
    private final RedisTemplate<String, Long> stringRedisTemplate;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof AuthorizationToken;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        ValueOperations<String, Long> valueOperations = stringRedisTemplate.opsForValue();
        AuthorizationToken token = (AuthorizationToken) authenticationToken;
        String redisKey = CacheUtils.SysUser.token(token.getToken());
        // Redis序列化会导致Long变Integer，所以需要强制转换
        Long userId = Convert.toLong(valueOperations.get(redisKey));
        if (Objects.isNull(userId)) {
            throw new ExpiredCredentialsException();
        }
        // 获取用户的信息
        SysUser sysUser = sysUserService.getById(userId);
        if (sysUser == null) {
            throw new UnknownAccountException();
        }
        if (this.isLocked(sysUser)) {
            throw new LockedAccountException();
        }
        sysUser.setPassword(null);
        // 用户每次请求后都刷新一次token
        valueOperations.getOperations().expire(redisKey, CacheUtils.SysUser.EXPIRE_TIME, TimeUnit.SECONDS);
        return new SimpleAuthenticationInfo(sysUser, token.getToken(), sysUser.getUsername());
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();

        Set<String> userRoles = sysRoleService.getRoleCodesByUserId(sysUser.getId());
        Set<String> userPermissions = sysMenuService.getUserPermissionsByUserId(sysUser.getId(), sysUser.isSuperAdmin());

        info.setRoles(userRoles);
        info.setStringPermissions(userPermissions);
        return info;
    }

    /**
     * 账户是否锁定
     *
     * @param sysUserEntity
     * @return
     */
    private boolean isLocked(SysUser sysUserEntity) {
        return Const.SysUser.Status.LOCKED.status == sysUserEntity.getStatus();
    }

}
